
namespace ReplayLastMessageMessenger.ViewModel
{
    using System;
    using System.Collections.Generic;

    using Microsoft.Phone.Shell;

    /// <summary>
    /// This class contains static references to all the view models in the
    /// application and provides an entry point for the bindings.
    /// <para>
    /// Use the <strong>mvvmlocatorproperty</strong> snippet to add ViewModels
    /// to this locator.
    /// </para>
    /// <para>
    /// In Silverlight and WPF, place the ViewModelLocatorTemplate in the App.xaml resources:
    /// </para>
    /// <code>
    /// &lt;Application.Resources&gt;
    ///     &lt;vm:ViewModelLocatorTemplate xmlns:vm="clr-namespace:Nestoria.Phone.ViewModelBaseWp7"
    ///                                  x:Key="Locator" /&gt;
    /// &lt;/Application.Resources&gt;
    /// </code>
    /// <para>
    /// Then use:
    /// </para>
    /// <code>
    /// DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
    /// </code>
    /// <para>
    /// You can also use Blend to do all this with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// <para>
    /// In <strong>*WPF only*</strong> (and if databinding in Blend is not relevant), you can delete
    /// the Main property and bind to the ViewModelNameStatic property instead:
    /// </para>
    /// <code>
    /// xmlns:vm="clr-namespace:Nestoria.Phone.ViewModelBaseWp7"
    /// DataContext="{Binding Source={x:Static vm:ViewModelLocatorTemplate.ViewModelNameStatic}}"
    /// </code>
    /// </summary>
    public class ViewModelLocator : IDisposable
    {
        #region Constants and Fields

        /// <summary>
        /// The boot strapper.
        /// </summary>
        private readonly BootStrapper bootStrapper;

        /// <summary>
        /// The disposed.
        /// </summary>
        private bool disposed;

        /// <summary>
        /// The resolved instances.
        /// </summary>
        private IDictionary<Type, ViewModelInstance> resolvedInstances = new Dictionary<Type, ViewModelInstance>();

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            if (this.bootStrapper == null)
            {
                this.bootStrapper = new BootStrapper();
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets ChildViewModel.
        /// </summary>
        public ChildViewModel ChildViewModel
        {
            get
            {
                return this.ResolveInstance<ChildViewModel>();
            }
        }

        /// <summary>
        /// Gets MainViewModel.
        /// </summary>
        public MainViewModel MainViewModel
        {
            get
            {
                return this.ResolveInstance<MainViewModel>();
            }
        }

        #endregion

        #region Implemented Interfaces

        #region IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// The dispose.
        /// </summary>
        /// <param name="disposing">
        /// The disposing.
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (this.disposed)
            {
                return;
            }

            if (disposing)
            {
                this.resolvedInstances.Clear();
                this.resolvedInstances = null;

                this.bootStrapper.Dispose();
            }

            this.disposed = true;
        }

        /// <summary>
        /// The resolve instance.
        /// </summary>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// </returns>
        private T ResolveInstance<T>() where T : ViewModelBaseWp7
        {
            if (this.resolvedInstances.ContainsKey(typeof(T)))
            {
                return (T)this.resolvedInstances[typeof(T)].ViewModel;
            }

            var viewModel = this.bootStrapper.Container.Resolve<T>();
            this.resolvedInstances.Add(viewModel.GetType(), new ViewModelInstance(viewModel));

            return viewModel;
        }

        #endregion

        /// <summary>
        /// The view model instance.
        /// </summary>
        internal class ViewModelInstance
        {
            #region Constructors and Destructors

            /// <summary>
            /// Initializes a new instance of the <see cref="ViewModelInstance"/> class.
            /// </summary>
            /// <param name="viewModel">
            /// The view model.
            /// </param>
            public ViewModelInstance(ViewModelBaseWp7 viewModel)
            {
                this.ViewModel = viewModel;

                if (PhoneApplicationService.Current.StartupMode == StartupMode.Activate)
                {
                    this.ViewModel.Activate();
                }

                PhoneApplicationService.Current.Deactivated += this.HandleDeactivation;
            }

            #endregion

            #region Properties

            /// <summary>
            /// Gets ViewModel.
            /// </summary>
            public ViewModelBaseWp7 ViewModel { get; private set; }

            #endregion

            #region Methods

            /// <summary>
            /// The handle deactivation.
            /// </summary>
            /// <param name="s">
            /// The s.
            /// </param>
            /// <param name="e">
            /// The e.
            /// </param>
            private void HandleDeactivation(object s, DeactivatedEventArgs e)
            {
                this.ViewModel.Deactivate();
            }

            #endregion
        }
    }
}